# 兼容直接脚本和模块启动：自动修正 sys.path
import sys
import os
sys.path.insert(0, os.path.dirname(__file__))
# webui.py

import gradio as gr
from typing import Dict, Any, List
import threading
import queue
import time
import traceback
# 尝试两种导入方式：当直接运行 `python src/webui.py` 时，
# sys.path[0] 通常为 `src`，这时包名应为 `agents`；
# 当以模块方式运行（例如 `python -m src.webui`）或测试中，
# 可能需要使用 `src.agents.runner`。这里做兼容处理。
from agents.runner import run_wealth_advisor, SAMPLE_CUSTOMER_PROFILES
from datetime import datetime
from io import StringIO

# 图片路径配置
ASSET_PATH = "./src/assets"
USER_AVATAR = f"{ASSET_PATH}/user_avatar.jpeg"
BOT_AVATAR = f"{ASSET_PATH}/bot_avatar.png"
BANNER_IMAGE = f"{ASSET_PATH}/logo.jpg"

QUESTIONS = [
    "我的投资组合中科技股占比是多少？",
    "今天上证指数的表现如何？",
    "根据当前市场情况，我应该如何调整投资组合以应对可能的经济衰退？",
    "考虑到我的退休目标，请评估我当前的投资策略并提供优化建议。",
    "请解释一下什么是ETF？",
    "我想为子女准备教育金，请帮我设计一个10年期的投资计划。",
]

def capture_output(func):
    """装饰器：捕获函数执行期间的输出"""
    def wrapper(*args, **kwargs):
        # 创建字符串缓冲区捕获输出
        output_buffer = StringIO()
        
        # 重定向标准输出
        old_stdout = sys.stdout
        sys.stdout = output_buffer
        
        try:
            # 执行原函数
            result = func(*args, **kwargs)
            
            # 获取捕获的输出
            captured_output = output_buffer.getvalue()
            
            # 恢复标准输出
            sys.stdout = old_stdout
            
            # 将输出作为额外返回值返回
            return result, captured_output
            
        except Exception as e:
            # 发生异常时也要恢复标准输出
            sys.stdout = old_stdout
            raise e
    
    return wrapper

@capture_output
def ask_wealth(user_query, customer_id):
    """调用 runner.run_wealth_advisor，带超时保护和异常捕获。

    如果后端处理超过 timeout 秒，将返回超时提示给前端，避免界面无响应。
    """
    timeout = 180.0  # 秒（增加超时以容忍慢模型响应）
    q: "queue.Queue" = queue.Queue()

    print(f"🔍 开始处理查询: {user_query}")
    customer_key = customer_id  # 比如 "customer1"
    if customer_key not in SAMPLE_CUSTOMER_PROFILES:
        return f"❌ 客户key无效，可选值: {list(SAMPLE_CUSTOMER_PROFILES.keys())}", []

    # 打印客户类型
    print(f"🧑💼 选择客户: {SAMPLE_CUSTOMER_PROFILES[customer_key]['risk_tolerance']} 投资者")
    
    print("\n⏳正在处理...\n")

    start_time = datetime.now()
    def target():
        try:
            res = run_wealth_advisor(user_query, customer_id)
            q.put(("ok", res))
        except Exception as e:
            tb = traceback.format_exc()
            print("[webui] run_wealth_advisor 异常:\n", tb)
            q.put(("err", str(e)))

    thread = threading.Thread(target=target, daemon=True)
    thread.start()
    print(f"[webui] 请求已提交，等待后端响应（timeout={timeout}s） 时间={time.strftime('%Y-%m-%d %H:%M:%S')}")

    try:
        status, payload = q.get(timeout=timeout)
    except queue.Empty:
        print(f"[webui] 请求超时：{timeout}s，query={user_query[:80]}")
        return f"[超时] 后端处理超过 {int(timeout)} 秒，请稍后重试。"

    if status == "err":
        return f"[错误] 后端异常: {payload}"

    result = payload
    answer = result.get("final_response", "未生成响应")
    if result.get("error"):
        answer = f"[错误] {result['error']}\n\n{answer}"

    end_time = datetime.now()
    process_time = (end_time - start_time).total_seconds()
    print(f"⏱️请求已处理完成，耗时：{process_time:.2f} 秒")

     # 显示处理模式
    process_mode = result.get("processing_mode", "未知")
    if process_mode == "reactive":
        print("🤖处理模式: 反应式- 快速响应简单查询")
    else:
        print("🤖处理模式: 深思熟虑- 深度分析复杂查询")

    print(f"📋 最终回答长度: {len(answer)} 字符")   
    
    return answer
def chat_fn(history, prompt,cust_id):

    if not prompt.strip():
        return history, ""
    
    # 处理查询并捕获输出
    final_result, process_log = ask_wealth(prompt,cust_id)

    # 构建带日志的完整回复
    log_section = f"<details><summary>🔄 处理日志 (点击展开/折叠)</summary>\n\n```\n{process_log}\n```\n</details>\n\n---\n\n**回答：**\n\n{final_result}"
    
    history.append([prompt, log_section])
                    
    return history, ""

with gr.Blocks(title="财富顾问") as demo:
    with gr.Row():
        #左侧栏
        with gr.Column(scale=1):
            
            try:
               
                gr.Image(BANNER_IMAGE, height=200, show_label=False)
            except Exception:
                # 如果图片加载失败，显示占位文本
                gr.Markdown("**财富顾问**")
            # 标题与副标题（居中，大字体/小字体）
            gr.HTML("<div id='wealth-helper-card'><h2 id='wealth-helper-title'>财富助手</h2><div id='wealth-helper-subtitle'>一个帮助你解决财富打理问题的助手，值得您信赖的好帮手</div></div>")

            customer = gr.Dropdown(label="请选择客户类型", choices=[
                ("平衡型投资者", "customer1"), ("进取型投资者", "customer2")
            ], value="customer1")

            gr.Markdown("#### 💬 推荐对话")
            
            # 创建6个推荐问题按钮
            question_buttons = []
            for i, question in enumerate(QUESTIONS):
                btn = gr.Button(
                    question,
                    size="sm",
                    variant="secondary",
                    min_width=200,
                    elem_classes="recommend-btn"
                )
                question_buttons.append(btn)
        #右侧栏        
        with gr.Column(scale=3):
            chatbot = gr.Chatbot(
                height=800,
                #type="messages",  # 添加这一行
                avatar_images=(USER_AVATAR, BOT_AVATAR)
            )
            inp = gr.Textbox(show_label=False, placeholder="输入您的问题…")
            #send_btn = gr.Button("发送", variant="primary")

    # 添加自定义CSS样式
    demo.css = """
   
    .recommend-btn {
        background: none !important;
        border: none !important;
        box-shadow: none !important;
        # color: #1890ff !important;
        text-align: left !important;
        # padding: 6px 0 !important;
        # margin: 4px 0 !important;
        font-size: 14px !important;
        font-weight: normal !important;
        text-decoration: none !important;
        cursor: pointer !important;
        width: 100% !important;
        justify-content: flex-start !important;
    }
    .recommend-btn:hover {
        background: #f5f5f5 !important;
        color: #1890ff !important;
        text-decoration: underline !important;
    }
    /* 调整头像大小 */
    .gradio-chatbot .avatar {
        width: 32px !important;
        height: 32px !important;
        border-radius: 50% !important;
    }
    """

    # 为每个推荐问题按钮绑定事件
    for btn in question_buttons:
        btn.click(
            fn=lambda q=btn.value: q, # 设置输入框内容并保持历史
            outputs=[inp]
        ).then(
            fn=chat_fn,
            inputs=[chatbot, inp, customer],
            outputs=[chatbot, inp]
        )

    # 回车 / 点击发送后清空输入框，并提交到 chat_fn 处理
    inp.submit(chat_fn, [chatbot, inp, customer], [chatbot, inp]).then(
        lambda: "", None, inp
    )

    #send_btn.click(bot, [chat, inp], [chat, inp])

if __name__ == "__main__":
    demo.launch()
